define(function(require, exports, module) {

  // var Backbone = require('backbone');
  var $ = require('jquery');
  var _ = require('lodash');
  // var jsonview = require('jsonview');
  var tmpl = require('text!../tmpl.html');
  
  var twgl = require('https://twgljs.org/dist/4.x/twgl-full.min.js')

  function main() {
    var $app = $('<div id="app">');
    $('#main').html($app);

    $app.append(tmpl);
    
    canvas1()
    canvas2()
  }
  
  
  function canvas1() {
    const gl = document.querySelector("#canvas1").getContext("webgl");
    const vs = `
    attribute vec4 position;
    attribute float u;
    varying float v_u;
    void main() {
      gl_Position = position;
      v_u = u;
    }
    `;
    const fs = `
    precision mediump float;
    varying float v_u;
    uniform float time;
    void main() {
      float green = fract(v_u + time);
      gl_FragColor = vec4(0, green, 0, 1);
    }
    `;
    
    // compile shaders, link program, look up uinforms
    const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
    
    // make some lines (calls gl.createBuffer, gl.bindBuffer, gl.bufferData 
    // for each array)
    const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
      position: {
        numComponents: 2,
        data: [ 
          -.5, -.5, 
           .7, -.3,
           .7, -.3,
          -.1,  .8,
          -.1,  .8,
          -.8,  .2,
        ],
      },
      u: {
        numComponents: 1, 
        data: [
          0, 1,  // these numbers define how many times
          0, 2,  // the pattern repeats for each line segment
          0, 3,  
        ],
      },
    });
    
    function render(time) {
      time *= 0.001; // seconds
      
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
      gl.clearColor(0, 0, 0, 1);
      gl.clear(gl.COLOR_BUFFER_BIT);
      
      gl.useProgram(programInfo.program);
      
      // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
      twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
      
      // calls gl.uniformXXX
      twgl.setUniforms(programInfo, { time: -time });
      
      // calls gl.drawArrays or gl.drawElements
      twgl.drawBufferInfo(gl, bufferInfo, gl.LINES);
      
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
  }
  
  function canvas2() {
    const m4 = twgl.m4;
    const gl = document.querySelector('#canvas2').getContext('webgl')
      
    const vs = `
    attribute vec4 position;
    attribute vec2 texcoord;
    uniform mat4 u_matrix;
    varying vec2 v_texcoord;
    void main() {
      gl_Position = u_matrix * position;
      v_texcoord = texcoord;
    }
    `;
    
    const fs = `
    precision mediump float;
    varying vec2 v_texcoord;
    uniform sampler2D u_tex;
    void main() {
      gl_FragColor = texture2D(u_tex, v_texcoord);
    }
    `;
    
    // compile shaders, link program, look up locations
    const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
      
    // gl.createBuffer, gl.bufferData for positions and texcoords of a cube
    const cubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
    // gl.createBuffer, gl.bufferData for positions and texcoords of a quad
    const quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl, 2);
      
    // all the normal stuff for setting up a texture
    const imageTexture = twgl.createTexture(gl, {
      src: 'https://i.imgur.com/ZKMnXce.png',
    });
      
    function makeFramebufferAndTexture(gl, width, height) {
      const framebuffer = gl.createFramebuffer();
      gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
      
      const texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture);
      gl.texImage2D(gl.TEXTURE_2D,
         0,       // level
         gl.RGBA, // internal format
         width,
         height,
         0,       // border
         gl.RGBA, // format
         gl.UNSIGNED_BYTE, // type
         null,    // data (no data needed)
      );
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      
      gl.framebufferTexture2D(
         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
         gl.TEXTURE_2D, texture, 0 /* level */);
    
      // note: depending on what you're rendering you might want to atttach
      // a depth renderbuffer or depth texture. See linked article
      
      return {
        framebuffer,
        texture,
        width,
        height,
      };
    }
    
    function bindFramebufferAndSetViewport(gl, fbi) {
      gl.bindFramebuffer(gl.FRAMEBUFFER, fbi ? fbi.framebuffer : null);
      const {width, height} = fbi || gl.canvas;
      gl.viewport(0, 0, width, height);
    }
      
    let fbiA = makeFramebufferAndTexture(gl, 512, 512);
    let fbiB = makeFramebufferAndTexture(gl, 512, 512);
    
    function drawImageAndPreviousFrameToTextureB() {
      bindFramebufferAndSetViewport(gl, fbiB);
      
      // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
      // for each attribute
      twgl.setBuffersAndAttributes(gl, programInfo, quadBufferInfo);
      
      // calls gl.activeTexture, gl.bindTexture, gl.uniform 
      twgl.setUniforms(programInfo, {
        u_tex: imageTexture,
        u_matrix: m4.identity(),
      });
      
      // calls gl.drawArrays or gl.drawElements
      twgl.drawBufferInfo(gl, quadBufferInfo);
      
      // ---------
      
      // draw previous cube texture into current cube texture
      {
        twgl.setUniforms(programInfo, {
          u_tex: fbiA.texture,
          u_matrix: m4.scaling([0.8, 0.8, 1]),
        });
        twgl.drawBufferInfo(gl, quadBufferInfo);
      }
    }    
      
    function drawTexturedCubeToTextureA(time) {
      // ---------   
      // draw cube to "new" dstFB using srcFB.texture on cube
      bindFramebufferAndSetViewport(gl, fbiA);
      gl.clear(gl.COLOR_BUFFER_BIT);
      
      twgl.setBuffersAndAttributes(gl, programInfo, cubeBufferInfo);
      
      {
        const fov = 60 * Math.PI / 180;
        const aspect = fbiA.width / fbiA.height;
        const near = 0.1;
        const far = 100;
        let mat = m4.perspective(fov, aspect, near, far); 
        mat = m4.translate(mat, [0, 0, -2]);
        mat = m4.rotateX(mat, time);
        mat = m4.rotateY(mat, time * 0.7);
      
        twgl.setUniforms(programInfo, {
          u_tex: fbiB.texture,
          u_matrix: mat,
        });
      }
      
      twgl.drawBufferInfo(gl, cubeBufferInfo);
    }
    
    function drawTextureAToCanvas() {
      // --------
      // draw dstFB.texture to canvas
      bindFramebufferAndSetViewport(gl, null);
      
      twgl.setBuffersAndAttributes(gl, programInfo, quadBufferInfo);
      
      {
        const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
        const near = -1;
        const far = 1;
        let mat = m4.ortho(-aspect, aspect, -1, 1, near, far);
      
        twgl.setUniforms(programInfo, {
          u_tex: fbiA.texture,
          u_matrix: mat,
        });
      }
      
      twgl.drawBufferInfo(gl, quadBufferInfo);
    }  
    
    function render(time) {
      time *= 0.001; // convert to seconds;
      
      twgl.resizeCanvasToDisplaySize(gl.canvas);
      
      gl.enable(gl.DEPTH_TEST);
      gl.enable(gl.CULL_FACE);
      
      // there's only one shader program so let's set it here
      gl.useProgram(programInfo.program);
    
      drawImageAndPreviousFrameToTextureB();
      drawTexturedCubeToTextureA(time);
      drawTextureAToCanvas();
    
      requestAnimationFrame(render);
    }
    requestAnimationFrame(render);
  }

  return main;
})
